home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / gr564s.zip / SRC / PARTIME.C < prev    next >
C/C++ Source or Header  |  1992-09-05  |  19KB  |  663 lines

  1. /*
  2.  * PARTIME        parse date/time string into a TM structure
  3.  *
  4.  * Returns:
  5.  *    0 if parsing failed
  6.  *    else time values in specified TM structure and zone (unspecified values
  7.  *        set to TMNULL)
  8.  * Notes:
  9.  *    This code is quasi-public; it may be used freely in like software.
  10.  *    It is not to be sold, nor used in licensed software without
  11.  *    permission of the author.
  12.  *    For everyone's benefit, please report bugs and improvements!
  13.  *     Copyright 1980 by Ken Harrenstien, SRI International.
  14.  *    (ARPANET: KLH @ SRI)
  15.  */
  16.  
  17. /* Hacknotes:
  18.  *    If parsing changed so that no backup needed, could perhaps modify
  19.  *        to use a FILE input stream.  Need terminator, though.
  20.  *    Perhaps should return 0 on success, else a non-zero error val?
  21.  */
  22.  
  23. /* $Log: partime.c,v $
  24.  * Revision 5.8  1992/07/28  16:12:44  eggert
  25.  * Fix time zones again.
  26.  *
  27.  * Revision 5.7  1992/05/31  08:29:18  eggert
  28.  * The special time zone LOCAL_TIME+1 now tells maketime that no zone was given.
  29.  *
  30.  * Revision 5.6  1991/08/19  03:13:55  eggert
  31.  * Update timezones.
  32.  *
  33.  * Revision 5.5  1991/04/21  11:58:18  eggert
  34.  * Don't put , just before } in initializer.
  35.  *
  36.  * Revision 5.4  1990/10/04  06:30:15  eggert
  37.  * Remove date vs time heuristics that fail between 2000 and 2400.
  38.  * Check for overflow when lexing an integer.
  39.  * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
  40.  *
  41.  * Revision 5.3  1990/09/24  18:56:31  eggert
  42.  * Update timezones.
  43.  *
  44.  * Revision 5.2  1990/09/04  08:02:16  eggert
  45.  * Don't parse two-digit years, because it won't work after 1999/12/31.
  46.  * Don't permit 'Aug Aug'.
  47.  *
  48.  * Revision 5.1  1990/08/29  07:13:49  eggert
  49.  * Be able to parse our own date format.  Don't assume year<10000.
  50.  *
  51.  * Revision 5.0  1990/08/22  08:12:40  eggert
  52.  * Switch to GMT and fix the bugs exposed thereby.  Update timezones.
  53.  * Ansify and Posixate.  Fix peekahead and int-size bugs.
  54.  *
  55.  * Revision 1.4  89/05/01  14:48:46  narten
  56.  * fixed #ifdef DEBUG construct
  57.  * 
  58.  * Revision 1.3  88/08/28  14:53:40  eggert
  59.  * Remove unportable "#endif XXX"s.
  60.  * 
  61.  * Revision 1.2  87/03/27  14:21:53  jenkins
  62.  * Port to suns
  63.  * 
  64.  * Revision 1.1  82/05/06  11:38:26  wft
  65.  * Initial revision
  66.  * 
  67.  */
  68.  
  69. #include "rcsbase.h"
  70.  
  71. libId(partId, "$Id: partime.c,v 5.8 1992/07/28 16:12:44 eggert Exp $")
  72.  
  73. #define given(v) (0 <= (v))
  74. #define TMNULL (-1) /* Items not given are given this value */
  75. #define TZ_OFFSET (24*60) /* TMNULL  <  zone_offset - TZ_OFFSET */
  76.  
  77. struct tmwent {
  78.     char const *went;
  79.     short wval;
  80.     char wflgs;
  81.     char wtype;
  82. };
  83.     /* wflgs */
  84. #define TWTIME 02    /* Word is a time value (absence implies date) */
  85. #define TWDST  04    /* Word is a DST-type timezone */
  86.     /* wtype */
  87. #define TM_MON    1    /* month name */
  88. #define TM_WDAY    2    /* weekday name */
  89. #define TM_ZON    3    /* time zone name */
  90. #define TM_LT    4    /* local time */
  91. #define TM_DST    5    /* daylight savings time */
  92. #define TM_12    6    /* AM, PM, NOON, or MIDNIGHT */
  93.     /* wval (for wtype==TM_12) */
  94. #define T12_AM 1
  95. #define T12_PM 2
  96. #define T12_NOON 12
  97. #define T12_MIDNIGHT 0
  98.  
  99. #define LOCAL_TIME (48*60) /* local time magic number -- see maketime() */
  100.  
  101. static struct tmwent const tmwords [] = {
  102.     {"january",      0, 0, TM_MON},
  103.     {"february",     1, 0, TM_MON},
  104.     {"march",        2, 0, TM_MON},
  105.     {"april",        3, 0, TM_MON},
  106.     {"may",          4, 0, TM_MON},
  107.     {"june",         5, 0, TM_MON},
  108.     {"july",         6, 0, TM_MON},
  109.     {"august",       7, 0, TM_MON},
  110.     {"september",    8, 0, TM_MON},
  111.     {"october",      9, 0, TM_MON},
  112.     {"november",     10, 0, TM_MON},
  113.     {"december",     11, 0, TM_MON},
  114.  
  115.     {"sunday",       0, 0, TM_WDAY},
  116.     {"monday",       1, 0, TM_WDAY},
  117.     {"tuesday",      2, 0, TM_WDAY},
  118.     {"wednesday",    3, 0, TM_WDAY},
  119.     {"thursday",     4, 0, TM_WDAY},
  120.     {"friday",       5, 0, TM_WDAY},
  121.     {"saturday",     6, 0, TM_WDAY},
  122.  
  123.     {"gmt",      0*60,    TWTIME,    TM_ZON}, /* Greenwich Mean */
  124.     {"utc",      0*60,    TWTIME,    TM_ZON}, /* Coordinated Universal */
  125.     {"cut",      0*60,    TWTIME,    TM_ZON}, /* " */
  126.     {"ut",      0*60,    TWTIME,    TM_ZON}, /* Universal */
  127.  
  128.     {"nzst",-12*60,    TWTIME,    TM_ZON}, /* New Zealand */
  129.     {"nzdt",-12*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  130.     {"jst",    - 9*60,    TWTIME,    TM_ZON}, /* Japan */
  131.     {"kst",    - 9*60,    TWTIME,    TM_ZON}, /* Korea */
  132.     {"kdt",    - 9*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  133.     {"ist",    - 5*60-30, TWTIME,    TM_ZON}, /* India */
  134.     {"eet",    - 2*60,    TWTIME,    TM_ZON}, /* Eastern Europe */
  135.     {"cet",    - 1*60,    TWTIME,    TM_ZON}, /* Central Europe */
  136.     {"met",    - 1*60,    TWTIME,    TM_ZON}, /* Middle Europe */
  137.     {"wet",      0*60,    TWTIME,    TM_ZON}, /* Western Europe */
  138.     {"bst",      0*60,    TWTIME+TWDST,TM_ZON}, /* British Summer */
  139.     {"nst",      3*60+30, TWTIME,    TM_ZON}, /* Newfoundland */
  140.     {"ndt",      3*60+30, TWTIME+TWDST,TM_ZON}, /* " Daylight */
  141.     {"ast",      4*60,    TWTIME,    TM_ZON}, /* Atlantic */
  142.     {"adt",      4*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  143.     {"est",      5*60,    TWTIME,    TM_ZON}, /* Eastern */
  144.     {"edt",      5*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  145.     {"cst",      6*60,    TWTIME,    TM_ZON}, /* Central */
  146.     {"cdt",      6*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  147.     {"mst",      7*60,    TWTIME,    TM_ZON}, /* Mountain */
  148.     {"mdt",      7*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  149.     {"pst",      8*60,    TWTIME,    TM_ZON}, /* Pacific */
  150.     {"pdt",      8*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  151.     {"akst",  9*60,    TWTIME,    TM_ZON}, /* Alaska */
  152.     {"akdt",  9*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  153.     {"hast", 10*60,    TWTIME,    TM_ZON}, /* Hawaii-Aleutian */
  154.     {"hadt", 10*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  155.     {"hst",     10*60,    TWTIME,    TM_ZON}, /* Hawaii */
  156.  
  157. #if 0
  158.     /*
  159.      * The following names are duplicates or are not well attested.
  160.      */
  161.     {"?",    -12*60-45, TWTIME,    TM_ZON}, /* Chatham Is */
  162.     {"?",    -11*60-30, TWTIME+TWDST,TM_ZON}, /* Norfolk I */
  163.     {"?",    -11*60,    TWTIME,    TM_ZON}, /* Vanuatu */
  164.     {"?",    -11*60,    TWTIME+TWDST,TM_ZON}, /* " Summer */
  165.     {"est",    -10*60,    TWTIME,    TM_ZON}, /* Eastern Australia */
  166.     {"est",    -10*60,    TWTIME+TWDST,TM_ZON}, /* " Summer */
  167.     {"cst",    - 9*60-30, TWTIME,    TM_ZON}, /* Central Australia */
  168.     {"cst",    - 9*60-30, TWTIME+TWDST,TM_ZON}, /* " Summer */
  169.     {"cst",    - 8*60,    TWTIME,    TM_ZON}, /* China */
  170.     {"cdt",    - 8*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  171.     {"hkt",    - 8*60,    TWTIME,    TM_ZON}, /* Hong Kong */
  172.     {"sst",    - 8*60,    TWTIME,    TM_ZON}, /* Singapore */
  173.     {"wst",    - 8*60,    TWTIME,    TM_ZON}, /* Western Australia */
  174.     {"wst",    - 8*60,    TWTIME+TWDST,TM_ZON}, /* " Summer */
  175.     {"?",    - 7*60,    TWTIME,    TM_ZON}, /* Thailand */
  176.     {"?",    - 6*60-30, TWTIME,    TM_ZON}, /* Burma */
  177.     {"?",    - 6*60,    TWTIME,    TM_ZON}, /* Bangladesh */
  178.     {"?",    - 5*60-45, TWTIME,    TM_ZON}, /* Nepal */
  179.     {"?",    - 5*60,    TWTIME,    TM_ZON}, /* Pakistan */
  180.     {"?",    - 4*60-30, TWTIME,    TM_ZON}, /* Afghanistan */
  181.     {"?",    - 4*60,    TWTIME,    TM_ZON}, /* Oman */
  182.     {"ist",    - 3*60-30, TWTIME,    TM_ZON}, /* Iran */
  183.     {"idt",    - 3*60-30, TWTIME+TWDST,TM_ZON}, /* " Daylight */
  184.     {"?",    - 3*60,    TWTIME,    TM_ZON}, /* Iraq */
  185.     {"?",    - 3*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  186.     {"ist",    - 2*60,    TWTIME,    TM_ZON}, /* Israel */
  187.     {"idt",    - 2*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  188.     {"mez",    - 1*60,    TWTIME,    TM_ZON}, /* Mittel-Europaeische Zeit */
  189.     {"mesz",- 1*60,    TWTIME+TWDST,TM_ZON}, /* " Sommerzeit */
  190.     {"ast",      1*60,    TWTIME,    TM_ZON}, /* Azores */
  191.     {"adt",      1*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  192.     {"fst",      2*60,    TWTIME,    TM_ZON}, /* Fernando de Noronha */
  193.     {"fdt",      2*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  194.     {"est",      3*60,    TWTIME,    TM_ZON}, /* Eastern Brazil */
  195.     {"edt",      3*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  196.     {"cst",      4*60,    TWTIME,    TM_ZON}, /* Chile */
  197.     {"cdt",      4*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  198.     {"wst",      4*60,    TWTIME,    TM_ZON}, /* Western Brazil */
  199.     {"wdt",      4*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  200.     {"ast",      5*60,    TWTIME,    TM_ZON}, /* Acre Brazil */
  201.     {"adt",      5*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  202.     {"cst",      5*60,    TWTIME,    TM_ZON}, /* Cuba */
  203.     {"cdt",      5*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  204.     {"est",      6*60,    TWTIME,    TM_ZON}, /* Rapa Nui (Easter) I */
  205.     {"edt",      6*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  206.     {"yst",      9*60,    TWTIME,    TM_ZON}, /* Yukon */
  207.     {"ydt",      9*60,    TWTIME+TWDST,TM_ZON}, /* " Daylight */
  208.     {"?",      9*60+30, TWTIME,    TM_ZON}, /* Marquesas Is */
  209.     {"sst",     11*60,    TWTIME,    TM_ZON}, /* Samoa */
  210.     {"?",     12*60,    TWTIME,    TM_ZON}, /* Kwajalein */
  211. #endif
  212.  
  213.     {"lt",           0, TWTIME, TM_LT},       /* local time */
  214.     {"dst",          1*60, TWTIME, TM_DST},      /* daylight savings time */
  215.     {"ddst",         2*60, TWTIME, TM_DST},      /* double dst */
  216.  
  217.     {"am",           T12_AM,    TWTIME, TM_12},
  218.     {"pm",           T12_PM,    TWTIME, TM_12},
  219.     {"noon",         T12_NOON,    TWTIME, TM_12},
  220.     {"midnight",     T12_MIDNIGHT,    TWTIME, TM_12},
  221.  
  222.     {0, 0, 0, 0}    /* Zero entry to terminate searches */
  223. };
  224.  
  225. struct token {
  226.     char const *tcp;/* pointer to string */
  227.     int tcnt;    /* # chars */
  228.     char tbrk;    /* "break" char */
  229.     char tbrkl;    /* last break char */
  230.     char tflg;    /* 0 = alpha, 1 = numeric */
  231.     union {         /* Resulting value; */
  232.         int tnum;/* either a #, or */
  233.         struct tmwent const *ttmw;/* a ptr to a tmwent.  */
  234.     } tval;
  235. };
  236.  
  237. static struct tmwent const*ptmatchstr P((char const*,int,struct tmwent const*));
  238. static int pt12hack P((struct tm *,int));
  239. static int ptitoken P((struct token *));
  240. static int ptstash P((int *,int));
  241. static int pttoken P((struct token *));
  242.  
  243.     static int
  244. goodzone(t, offset, am)
  245.     register struct token const *t;
  246.     int offset;
  247.     int *am;
  248. {
  249.     register int m;
  250.     if (
  251.         t->tflg  &&
  252.         t->tcnt == 4+offset  &&
  253.         (m = t->tval.tnum) <= 2400  &&
  254.         isdigit(t->tcp[offset]) &&
  255.         (m%=100) < 60
  256.     ) {
  257.         m += t->tval.tnum/100 * 60;
  258.         if (t->tcp[offset-1]=='+')
  259.             m = -m;
  260.         *am = m;
  261.         return 1;
  262.     }
  263.     return 0;
  264. }
  265.  
  266.     int
  267. partime(astr, atm, zone)
  268. char const *astr;
  269. register struct tm *atm;
  270. int *zone;
  271. {
  272.     register int i;
  273.     struct token btoken, atoken;
  274.     int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
  275.     register char const *cp;
  276.     register char ch;
  277.     int ord, midnoon;
  278.     int *atmfield, dst, m;
  279.     int got1 = 0;
  280.  
  281.     atm->tm_sec = TMNULL;
  282.     atm->tm_min = TMNULL;
  283.     atm->tm_hour = TMNULL;
  284.     atm->tm_mday = TMNULL;
  285.     atm->tm_mon = TMNULL;
  286.     atm->tm_year = TMNULL;
  287.     atm->tm_wday = TMNULL;
  288.     atm->tm_yday = TMNULL;
  289.     midnoon = TMNULL;        /* and our own temp stuff */
  290.     zone_offset = TMNULL;
  291.     dst = TMNULL;
  292.     btoken.tcnt = btoken.tbrk = 0;
  293.     btoken.tcp = astr;
  294.  
  295.     for (;  ptitoken(&btoken);  got1=1) {
  296.     if(btoken.tflg == 0)        /* Alpha? */
  297.       {     i = btoken.tval.ttmw->wval;
  298.         switch (btoken.tval.ttmw->wtype) {
  299.           default:
  300.             return 0;
  301.           case TM_MON:
  302.             atmfield = &atm->tm_mon;
  303.             break;
  304.           case TM_WDAY:
  305.             atmfield = &atm->tm_wday;
  306.             break;
  307.           case TM_DST:
  308.             atmfield = &dst;
  309.             break;
  310.           case TM_LT:
  311.             if (ptstash(&dst, 0))
  312.                 return 0;
  313.             i = LOCAL_TIME;
  314.             /* fall into */
  315.           case TM_ZON:
  316.             i += TZ_OFFSET;
  317.             if (btoken.tval.ttmw->wflgs & TWDST)
  318.                 if (ptstash(&dst, 60))
  319.                     return 0;
  320.             /* Peek ahead for offset immediately afterwards. */
  321.             if (
  322.                 (btoken.tbrk=='-' || btoken.tbrk=='+') &&
  323.                 (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
  324.                 goodzone(&atoken, 0, &m)
  325.             ) {
  326.                 i += m;
  327.                 btoken = atoken;
  328.             }
  329.             atmfield = &zone_offset;
  330.             break;
  331.           case TM_12:
  332.             atmfield = &midnoon;
  333.         }
  334.         if (ptstash(atmfield, i))
  335.             return(0);        /* ERR: val already set */
  336.         continue;
  337.       }
  338.  
  339.     /* Token is number.  Lots of hairy heuristics. */
  340.     if (!isdigit(*btoken.tcp)) {
  341.         if (!goodzone(&btoken, 1, &m))
  342.             return 0;
  343.         zone_offset = TZ_OFFSET + m;
  344.         continue;
  345.     }
  346.  
  347.     i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  348.     if (btoken.tcnt == 3)    /*  3 digits = HMM   */
  349.       {
  350. hhmm4:        if (ptstash(&atm->tm_min, i%100))
  351.             return(0);        /* ERR: min conflict */
  352.         i /= 100;
  353. hh2:            if (ptstash(&atm->tm_hour, i))
  354.             return(0);        /* ERR: hour conflict */
  355.         continue;
  356.       }
  357.  
  358.     if (4 < btoken.tcnt)
  359.         goto year4; /* far in the future */
  360.     if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  361.       {    if (given(atm->tm_year)) goto hhmm4;    /* Already got yr? */
  362.         if (given(atm->tm_hour)) goto year4;    /* Already got hr? */
  363.         if(btoken.tbrk == ':')            /* HHMM:SS ? */
  364.             if ( ptstash(&atm->tm_hour, i/100)
  365.               || ptstash(&atm->tm_min, i%100))
  366.                 return(0);        /* ERR: hr/min clash */
  367.             else goto coltm2;        /* Go handle SS */
  368.         if(btoken.tbrk != ',' && btoken.tbrk != '/'
  369.           && (atoken=btoken, ptitoken(&atoken))    /* Peek */
  370.           && ( atoken.tflg
  371.              ? !isdigit(*atoken.tcp)
  372.              : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
  373.             goto hhmm4;
  374.         goto year4;            /* Give up, assume year. */
  375.       }
  376.  
  377.     /* From this point on, assume tcnt == 1 or 2 */
  378.     /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
  379.     if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  380.         goto coltime;        /*  must be part of time. */
  381.     if (31 < i)
  382.         return 0;
  383.  
  384.     /* Check for numerical-format date */
  385.     for (cp = "/-."; ch = *cp++;)
  386.       {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  387.         if(btoken.tbrk == ch)            /* "NN-" */
  388.           {    if(btoken.tbrkl != ch)
  389.               {
  390.                 atoken = btoken;
  391.                 atoken.tcnt++;
  392.                 if (ptitoken(&atoken)
  393.                   && atoken.tflg == 0
  394.                   && atoken.tval.ttmw->wtype == TM_MON)
  395.                     goto dd2;
  396.                 if(ord)goto mm2; else goto dd2; /* "NN-" */
  397.               }                /* "-NN-" */
  398.             if (!given(atm->tm_mday)
  399.               && given(atm->tm_year))    /* If "YYYY-NN-" */
  400.                 goto mm2;        /* then always MM */
  401.             if(ord)goto dd2; else goto mm2;
  402.           }
  403.         if(btoken.tbrkl == ch            /* "-NN" */
  404.           && given(ord ? atm->tm_mon : atm->tm_mday))
  405.             if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
  406.                 if(ord)goto dd2; else goto mm2;
  407.       }
  408.  
  409.     /* Now reduced to choice between HH and DD */
  410.     if (given(atm->tm_hour)) goto dd2;    /* Have hour? Assume day. */
  411.     if (given(atm->tm_mday)) goto hh2;    /* Have day? Assume hour. */
  412.     if (given(atm->tm_mon)) goto dd2;    /* Have month? Assume day. */
  413.     if(i > 24) goto dd2;            /* Impossible HH means DD */
  414.     atoken = btoken;
  415.     if (!ptitoken(&atoken))            /* Read ahead! */
  416.         if(atoken.tval.tnum) return(0); /* ERR: bad token */
  417.         else goto dd2;            /* EOF, assume day. */
  418.     if ( atoken.tflg
  419.        ? !isdigit(*atoken.tcp)
  420.        : atoken.tval.ttmw->wflgs & TWTIME)
  421.         /* If next token is a time spec, assume hour */
  422.         goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  423.  
  424. dd2:    if (ptstash(&atm->tm_mday, i))    /* Store day (1 based) */
  425.         return(0);
  426.     continue;
  427.  
  428. mm2:    if (ptstash(&atm->tm_mon, i-1))    /* Store month (make zero based) */
  429.         return(0);
  430.     continue;
  431.  
  432. year4:    if ((i-=1900) < 0  ||  ptstash(&atm->tm_year, i)) /* Store year-1900 */
  433.         return(0);        /* ERR: year conflict */
  434.     continue;
  435.  
  436.     /* Hack HH:MM[[:]SS] */
  437. coltime:
  438.     if (ptstash(&atm->tm_hour, i)) return 0;
  439.     if (!ptitoken(&btoken)) {
  440.         got1 = 1;
  441.         break;
  442.     }
  443.     if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  444.     if(btoken.tcnt == 4)        /* MMSS */
  445.         if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
  446.           || ptstash(&atm->tm_sec, btoken.tval.tnum%100))
  447.             return(0);
  448.         else continue;
  449.     if(btoken.tcnt != 2
  450.       || ptstash(&atm->tm_min, btoken.tval.tnum))
  451.         return(0);        /* ERR: MM bad */
  452.     if (btoken.tbrk != ':') continue;    /* Seconds follow? */
  453. coltm2:    if (!ptitoken(&btoken)) {
  454.         got1 = 1;
  455.         break;
  456.     }
  457.     if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  458.       || ptstash(&atm->tm_sec, btoken.tval.tnum))
  459.         return(0);        /* ERR: SS bad */
  460.     }
  461.     if (btoken.tval.tnum)
  462.     return 0; /* read error */
  463.  
  464.     /* EOF, wrap up */
  465.     if (given(midnoon)  &&  !pt12hack(atm, midnoon))
  466.     return 0;
  467.     *zone = given(zone_offset)
  468.     ? zone_offset - TZ_OFFSET - (given(dst) ? dst : 0)
  469.     : LOCAL_TIME + 1; /* tell maketime that no zone was given */
  470.     return got1;
  471. }
  472.  
  473. /* Store date/time value, return 0 if successful.
  474.  * Fail if entry is already set.
  475.  */
  476.     static int
  477. ptstash(adr,val)
  478. int *adr;
  479. int val;
  480. {    register int *a;
  481.     if (given(*(a=adr)))
  482.         return 1;
  483.     *a = val;
  484.     return(0);
  485. }
  486.  
  487. /* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
  488.  * just prior to returning from partime.
  489.  */
  490.     static int
  491. pt12hack(tm, aval)
  492. register struct tm *tm;
  493. register int aval;
  494. {    register int h = tm->tm_hour;
  495.     switch (aval) {
  496.       case T12_AM:
  497.       case T12_PM:
  498.         if (h > 12)
  499.             return 0;
  500.         if (h == 12)
  501.             tm->tm_hour = 0;
  502.         if (aval == T12_PM)
  503.             tm->tm_hour += 12;
  504.         break;
  505.       default:
  506.         if (0 < tm->tm_min  ||  0 < tm->tm_sec)
  507.             return 0;
  508.         if (!given(h) || h==12)
  509.             tm->tm_hour = aval;
  510.         else if (aval==T12_MIDNIGHT  &&  (h==0 || h==24))
  511.             return 0;
  512.     }
  513.     return 1;
  514. }
  515.  
  516. /* Get a token and identify it to some degree.
  517.  * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  518.  * hit error of some sort
  519.  */
  520.  
  521.     static int
  522. ptitoken(tkp)
  523. register struct token *tkp;
  524. {
  525.     register char const *cp;
  526.     register int i, j, k;
  527.  
  528.     if (!pttoken(tkp))
  529. #ifdef DEBUG
  530.         {
  531.         VOID printf("EOF\n");
  532.         return(0);
  533.         }
  534. #else
  535.         return(0);
  536. #endif    
  537.     cp = tkp->tcp;
  538.  
  539. #ifdef DEBUG
  540.     VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
  541. #endif
  542.  
  543.     if (tkp->tflg) {
  544.         i = tkp->tcnt;
  545.         if (*cp == '+' || *cp == '-') {
  546.             cp++;
  547.             i--;
  548.         }
  549.         while (0 <= --i) {
  550.             j = tkp->tval.tnum*10;
  551.             k = j + (*cp++ - '0');
  552.             if (j/10 != tkp->tval.tnum  ||  k < j) {
  553.                 /* arithmetic overflow */
  554.                 tkp->tval.tnum = 1;
  555.                 return 0;
  556.             }
  557.             tkp->tval.tnum = k;
  558.         }
  559.     } else if (!(tkp->tval.ttmw  =  ptmatchstr(cp, tkp->tcnt, tmwords)))
  560.       {
  561. #ifdef DEBUG
  562.         VOID printf("Not found!\n");
  563. #endif
  564.         tkp->tval.tnum = 1;
  565.         return 0;
  566.       }
  567.  
  568. #ifdef DEBUG
  569.     if(tkp->tflg)
  570.         VOID printf("Val: %d.\n",tkp->tval.tnum);
  571.     else VOID printf("Found: \"%s\", val: %d, type %d\n",
  572.         tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  573. #endif
  574.  
  575.     return(1);
  576. }
  577.  
  578. /* Read token from input string into token structure */
  579.     static int
  580. pttoken(tkp)
  581. register struct token *tkp;
  582. {
  583.     register char const *cp;
  584.     register int c;
  585.     char const *astr;
  586.  
  587.     tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
  588.     tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  589.     tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  590.     tkp->tval.tnum = 0;
  591.  
  592.     while(c = *cp++)
  593.       {    switch(c)
  594.           {    case ' ': case '\t':    /* Flush all whitespace */
  595.             case '\r': case '\n':
  596.             case '\v': case '\f':
  597.                 if (!tkp->tcnt) {    /* If no token yet */
  598.                     tkp->tcp = cp;    /* ignore the brk */
  599.                     continue;    /* and go on. */
  600.                 }
  601.                 /* fall into */
  602.             case '(': case ')':    /* Perhaps any non-alphanum */
  603.             case '-': case ',':    /* shd qualify as break? */
  604.             case '+':
  605.             case '/': case ':': case '.':    /* Break chars */
  606.                 if(tkp->tcnt == 0)    /* If no token yet */
  607.                   {    tkp->tcp = cp;    /* ignore the brk */
  608.                     tkp->tbrkl = c;
  609.                       continue;    /* and go on. */
  610.                   }
  611.                 tkp->tbrk = c;
  612.                 return(tkp->tcnt);
  613.           }
  614.         if (!tkp->tcnt++) {        /* If first char of token, */
  615.             if (isdigit(c)) {
  616.                 tkp->tflg = 1;
  617.                 if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
  618.                     /* timezone is break+sign+digit */
  619.                     tkp->tcp--;
  620.                     tkp->tcnt++;
  621.                 }
  622.             }
  623.         } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
  624.             tkp->tbrk = c;
  625.             return --tkp->tcnt;    /* Wrong type, back up */
  626.         }
  627.       }
  628.     return(tkp->tcnt);        /* When hit EOF */
  629. }
  630.  
  631.  
  632.     static struct tmwent const *
  633. ptmatchstr(astr,cnt,astruc)
  634.     char const *astr;
  635.     int cnt;
  636.     struct tmwent const *astruc;
  637. {
  638.     register char const *cp, *mp;
  639.     register int c;
  640.     struct tmwent const *lastptr;
  641.     int i;
  642.  
  643.     lastptr = 0;
  644.     for(;mp = astruc->went; astruc += 1)
  645.       {    cp = astr;
  646.         for(i = cnt; i > 0; i--)
  647.           {
  648.             switch (*cp++ - (c = *mp++))
  649.               {    case 0: continue;    /* Exact match */
  650.                 case 'A'-'a':
  651.                     if (ctab[c] == Letter)
  652.                     continue;
  653.               }
  654.             break;
  655.           }
  656.         if(i==0)
  657.             if (!*mp) return astruc;    /* Exact match */
  658.             else if(lastptr) return(0);    /* Ambiguous */
  659.             else lastptr = astruc;        /* 1st ambig */
  660.       }
  661.     return lastptr;
  662. }
  663.